﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System.Diagnostics;
using System.Threading;
using SpriteGame.Rendering;

namespace SpriteGame.Effects.Particles
{
    public class ParticleManager
    {
        private static ParticleManager instance;
        public static ParticleManager Instance
        {
            get
            {
                if (instance == null)
                    instance = new ParticleManager();

                return instance;
            }
        }

        private static readonly object locke = new object();
        private static readonly object inputListLock = new object();

        private Stack<ParticleDef> particlesToAdd;
        private Stack<Particle> deadParticles;
        private List<Particle> particles;
        private SpriteBatch sb;
        Thread particleThread;

        private Stopwatch stopwatch;
        public ParticleManager()
        {
            stopwatch = new Stopwatch();
        }

        public void InitParticleManager(int _maxParticleCount, GraphicsDevice _graphics)
        {
            sb = new SpriteBatch(_graphics);
            particlesToAdd = new Stack<ParticleDef>();
            particles = new List<Particle>();
            deadParticles = new Stack<Particle>();

            for (int i = 0; i < _maxParticleCount; ++i)
            {
                deadParticles.Push(new Particle());
            }
            particleThread = new Thread(new ThreadStart(ParticleManager.Instance.StartThread));
            particleThread.Start();
        }

        public void SpawnParticle(Vector2 _position, Vector2 _velocity, Texture2D _texture, Color _color)
        {
            lock (inputListLock)
            {
                particlesToAdd.Push(new ParticleDef(_position, _velocity, _texture, _color));
            }
        }
        private const Int64 frameTime = (Int64)(1000 * (1.0f / 60.0f));
        public void StartThread()
        {
            stopwatch.Start();
            while (true)
            {
                Int64 time = 0;
                lock (locke)
                {
                    instance.update();

                    time = stopwatch.ElapsedMilliseconds;
                    stopwatch.Restart();
                }
                if(time < frameTime)
                    Thread.Sleep(TimeSpan.FromMilliseconds(frameTime - time));
            }
        }

        private void update()
        {
            lock (inputListLock)
            {
                Particle p;
                while (particlesToAdd.Count > 0)
                {
                    if (deadParticles.Count > 0)
                    {
                        p = deadParticles.Pop();
                    }
                    else
                    {
                        p = particles.First();
                        particles.Remove(p);
                        //particles.RemoveAt(particles.Count - 1);
                    }
                    ParticleDef pd = particlesToAdd.Pop();
                    p.InitParticle(pd.position, pd.velocity, pd.texture, pd.color);
                    particles.Add(p);
                }
            }

            for (int i = 0; i < particles.Count; ++i)
            {
                Particle p = particles[i];
                if (p.Alive)
                    p.Update();
                else
                {
                    particles.Remove(p);
                    deadParticles.Push(p);
                }
            }
        }

        public void Draw(ShapeRenderer _batch)
        {
            lock(locke)
            {
                _batch.BeginBatch();
                for (int i = 0; i < particles.Count; ++i)
                {
                    particles[i].Draw(_batch);
                }
                _batch.EndBatch();
            }
        }
    }
}
